#include <string.h>
#include <stdlib.h>

#include "define.h"
#include "http.h"

/*
 * On naming conventions:
 * 
 * HTTP/1.1 defines message-header as 
 * 
 * message-header = field-name ":" [ field-value ] CRLF
 * field-name     = token
 * field-value    = *( field-content | LWS )
 * 
 * HTTP/1.1 does not give a name name a group of all message-headers in a message.
 
 * local constants and vars

 * A table with major attributes for every known field. 
 * We calculate name lengths and reorganize this array on start up. 
 * After reorganization, field id can be used as an index to the table.
 */

typedef struct {
	int key;
	char *value;
}
key_value;

static key_value http_versions[] = {
	{ HTTP_VERSION_1_1         , "HTTP/1.1" },
	{ HTTP_VERSION_1_0         , "HTTP/1.0" },
	{ -1                       , NULL       }
};

static key_value http_methods[] = {
	{ HTTP_METHOD_GET          , "GET"      },
	{ HTTP_METHOD_POST         , "POST"     },
	{ HTTP_METHOD_HEAD         , "HEAD"     },
	{ HTTP_METHOD_PROPFIND     , "PROPFIND" },
	{ HTTP_METHOD_PROPPATCH    , "PROPPATCH"},
	{ HTTP_METHOD_REPORT       , "REPORT"   },
	{ HTTP_METHOD_OPTIONS      , "OPTIONS"  },
	{ HTTP_METHOD_MKCOL        , "MKCOL"    },
	{ HTTP_METHOD_PUT          , "PUT"      },
	{ HTTP_METHOD_DELETE       , "DELETE"   },
	{ HTTP_METHOD_COPY         , "COPY"     },
	{ HTTP_METHOD_MOVE         , "MOVE"     },
	{ HTTP_METHOD_LABEL        , "LABEL"    },
	{ HTTP_METHOD_CHECKOUT     , "CHECKOUT" },
	{ HTTP_METHOD_CHECKIN      , "CHECKIN"  },
	{ HTTP_METHOD_MERGE        , "MERGE"    },
	{ HTTP_METHOD_LOCK         , "LOCK"     },
	{ HTTP_METHOD_UNLOCK       , "UNLOCK"   },
	{ HTTP_METHOD_MKACTIVITY   , "MKACTIVITY"},
	{ HTTP_METHOD_UNCHECKOUT   , "UNCHECKOUT"},
	{ HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL"},
	{ HTTP_METHOD_CONNECT      , "CONNECT"  },
	{ -1                       , NULL       }
};

static key_value http_status[] = {
	{ 100, "Continue" },
	{ 101, "Switching Protocols" },
	{ 102, "Processing" }, /* WebDAV */
	{ 200, "OK" },
	{ 201, "Created" },
	{ 202, "Accepted" },
	{ 203, "Non-Authoritative Information" },
	{ 204, "No Content" },
	{ 205, "Reset Content" },
	{ 206, "Partial Content" },
	{ 207, "Multi-status" }, /* WebDAV */
	{ 300, "Multiple Choices" },
	{ 301, "Moved Permanently" },
	{ 302, "Found" },
	{ 303, "See Other" },
	{ 304, "Not Modified" },
	{ 305, "Use Proxy" },
	{ 306, "(Unused)" },
	{ 307, "Temporary Redirect" },
	{ 400, "Bad Request" },
	{ 401, "Unauthorized" },
	{ 402, "Payment Required" },
	{ 403, "Forbidden" },
	{ 404, "Not Found" },
	{ 405, "Method Not Allowed" },
	{ 406, "Not Acceptable" },
	{ 407, "Proxy Authentication Required" },
	{ 408, "Request Timeout" },
	{ 409, "Conflict" },
	{ 410, "Gone" },
	{ 411, "Length Required" },
	{ 412, "Precondition Failed" },
	{ 413, "Request Entity Too Large" },
	{ 414, "Request-URI Too Long" },
	{ 415, "Unsupported Media Type" },
	{ 416, "Requested Range Not Satisfiable" },
	{ 417, "Expectation Failed" },
	{ 422, "Unprocessable Entity" }, /* WebDAV */
	{ 423, "Locked" }, /* WebDAV */
	{ 424, "Failed Dependency" }, /* WebDAV */
	{ 426, "Upgrade Required" }, /* TLS */
	{ 500, "Internal Server Error" },
	{ 501, "Not Implemented" },
	{ 502, "Bad Gateway" },
	{ 503, "Service Not Available" },
	{ 504, "Gateway Timeout" },
	{ 505, "HTTP Version Not Supported" },
	{ 507, "Insufficient Storage" }, /* WebDAV */
	{ -1 , NULL }
};

static const char* get_value(key_value *entry, int key)
{
	while(entry->key != key && entry->key != -1)
		entry++;
	return entry->value;
}

static int get_key(key_value *entry, const char* value)
{
	while(entry->key != -1 && strncmp(entry->value, value, strlen(entry->value)) != 0)
		entry++;
	return entry->key;
}

connection* connection_init(connection *connect, tcp_client *client)
{
	if(connect == NULL) {
		connect = (connection*)malloc(sizeof(connection));
		memset(connect, 0x0, sizeof(connection));
		connect->alloc = 1;
	} else {
		memset(connect, 0x0, sizeof(connection));
	}

	/* response initialize */
	connect->response.entry =
	connect->response.tail = new_pair(HTTP_HEADER_SERVER, QUEUE_PROJECT_NAME" "QUEUE_PEOJECT_VERSION);
	http_pair_push(&(connect->response.tail), new_pair(HTTP_HEADER_CONTENT_TYPE , DEFAULT_CONTENT_TYPE));

	connect->response.content = (char*)malloc(DEFAULT_LIMIT_RESPONSE_DATASIZE+1);
	connect->client = client;
	return connect;
}

void connection_free(connection **connect)
{
	/* free all request header pair */
	http_pair *next;
	while((*connect)->request.entry != NULL)
	{
		next = (*connect)->request.entry->next;
		free((*connect)->request.entry->name );
		free((*connect)->request.entry->value);
		free((*connect)->request.entry);
		(*connect)->request.entry = next;
	}
	if((*connect)->request.content != NULL)
		free((*connect)->request.content);	
	if((*connect)->request.path    != NULL)
		free((*connect)->request.path);
	
	/* free all response header pair */
	while((*connect)->response.entry != NULL)
	{
		next = (*connect)->response.entry->next;
		free((*connect)->response.entry->name );
		free((*connect)->response.entry->value);
		free((*connect)->response.entry);
		(*connect)->response.entry = next;
	}
	free((*connect)->response.content);
	if((*connect)->alloc == 1)
		free(*connect);
	*connect = NULL;
}

void connection_clear(connection *connect)
{
	/* free all request header pair */
	http_pair *next;
	while(connect->request.entry != NULL)
	{
		next = connect->request.entry->next;
		free(connect->request.entry->name );
		free(connect->request.entry->value);
		free(connect->request.entry);
		connect->request.entry = next;
	}

	if(connect->request.content != NULL)
		free(connect->request.content);	
	if(connect->request.path != NULL)
		free(connect->request.path);
	
	/* free all response header pair */
	while(connect->response.entry != NULL)
	{
		next = connect->response.entry->next;
		free(connect->response.entry->name );
		free(connect->response.entry->value);
		free(connect->response.entry);
		connect->response.entry = next;
	}
	memset(connect->response.content, 0x0, DEFAULT_LIMIT_RESPONSE_DATASIZE + 1);
	connect->response.entry =
	connect->response.tail = new_pair(HTTP_HEADER_SERVER, QUEUE_PROJECT_NAME" "QUEUE_PEOJECT_VERSION);
	http_pair_push(&(connect->response.tail), new_pair(HTTP_HEADER_CONTENT_TYPE , DEFAULT_CONTENT_TYPE));
}

http_pair* new_pair(const char *name, const char *value)
{
	http_pair* pair;
	pair = (http_pair*)malloc(sizeof(http_pair));
	pair->name  = (char*)malloc(strlen(name ) + 1);
	pair->value = (char*)malloc(strlen(value) + 1);

	strcpy(pair->name , name );
	strcpy(pair->value, value);
	pair->next = NULL;
	return pair;
}

const char* http_stat_message(int status)
{
	return get_value(http_status, status);
}

const char* http_method_message(int method)
{
	return get_value(http_methods, method);
}

const char* http_version_message(int version)
{
	return get_value(http_versions, version);
}

int http_method_code(const char* message)
{
	return get_key(http_methods, message);
}
http_pair* http_pair_find(const char* name, http_pair* entry)
{
	while(entry != NULL && strcmp(entry->name, name) != 0)
		entry = entry->next;
	return entry;
}

http_pair* http_pair_push(http_pair** tail, http_pair *newpair)
{
	if(*tail == NULL)
		return *tail = newpair;

	http_pair *ptr = *tail;
	while(ptr->next != NULL)
		ptr = ptr->next;
	return *tail = ptr->next = newpair;
}

